import { asType } from '@gnlab/utils'
import { Tag, Input, Typography, Image } from 'antd'
import { ReactComponent as FileIcon } from '@/desktop/assets/file.svg'
import { ReactComponent as MpIcon } from '@/desktop/assets/mini-program.svg'
import { ReactComponent as VideonumIcon } from '@/desktop/assets/videonum.svg'
import { Video } from '@/desktop/components'
import GroupItem from '@/desktop/components/GroupItem'
import { Lazy, LazyImage } from '@/desktop/components/lazy'
import Link from '@/desktop/components/link'
import  defaultAvatar  from '@/desktop/assets/avatar.jpg'
import formatAssetUrl from '@/desktop/logics/format-asset-url'
import { useStreams } from '@/lib/utils'
import services from '@/services'
import { wordTitleMap, WordType } from '@/services/common/definition'
import type { WordTypes, TextWord, Word } from '@/services/common/definition'
import type { GroupContact } from '@/services/contact-types/groups'
import { ContactType } from '@/services/contacts'
import './index.module.less'
import { fileStyle } from './styles'

export function PreviewWord(props: {
  word: Word
  onUpdate?: (word: Word) => void // 传入此回调代表当前环境允许“即时编辑”，支持编辑的话术类型会将编辑结果会实时传给此回调
  className?: string
}) {
  const { word, onUpdate, className } = props
  if (!asType<TextWord>(word)) return null

  const Renderer = rendererMap[word.type]

  // 以防万一，如果出现了不合法的 type，不显示
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions
  if (!Renderer) return null

  return <div styleName="preview-word" className={className}>
    <div styleName="title-column">
      <Tag styleName="title">{wordTitleMap[word.type]}</Tag>
    </div>

    <div styleName="content">
      <Renderer word={word} onUpdate={onUpdate} />
    </div>
  </div>
}

// ================================================

type Renderer<T extends Word> = (props: { word: T; onUpdate?: (word: T) => void }) => JSX.Element
const rendererMap: { [type in WordType]: Renderer<WordTypes[type]> } = {
  [WordType.Text]: RenderText,
  [WordType.Images]: RenderImages,
  [WordType.Video]: RenderVideo,
  [WordType.Audio]: RenderAudio,
  [WordType.File]: RenderFile,
  [WordType.LinkCard]: RenderLinkCard,
  [WordType.PersonCard]: RenderPersonCard,
  [WordType.MiniProgram]: RenderMiniProgram,
  [WordType.Videonum]: RenderVideonum,
  [WordType.GroupInvite]: RenderGroupInvite,
  [WordType.GroupAt]: RenderGroupAt,
}

function RenderText(props: {
  word: WordTypes[WordType.Text]
  onUpdate?: (item: WordTypes[WordType.Text]) => void
}) {
  const { word, onUpdate } = props

  if (!onUpdate) {
    return <div styleName="word-text">{word.text}</div>
  }

  return <Input.TextArea
    styleName="word-text-editable"
    value={word.text}
    onChange={e => onUpdate({ ...word, text: e.target.value })}
    rows={5}
  />
}

function RenderImages(props: { word: WordTypes[WordType.Images] }) {
  const { word } = props
  return <div styleName="word-images">
    {word.images.map((url, index) => <Lazy key={index}>
      <Image key={url} src={formatAssetUrl(url)} loading="lazy" />
    </Lazy>)}
  </div>
}

function RenderVideo(props: { word: WordTypes[WordType.Video] }) {
  const { word } = props
  return <Lazy>
    <Video styleName="word-video" key={word.url} src={formatAssetUrl(word.url)} />
  </Lazy>
}

function RenderAudio(props: { word: WordTypes[WordType.Audio] }) {
  const { word } = props
  return <audio styleName="word-audio" controls key={word.url} src={formatAssetUrl(word.url)}>
    当前浏览器无法播放此音频
  </audio>
}

function RenderFile(props: { word: WordTypes[WordType.File] }) {
  const { word } = props
  return <div css={fileStyle}>
    <aside>
      <div className="name">{word.name}</div>
      <a href={formatAssetUrl(word.url)} download={word.name}>
        下载
      </a>
    </aside>
    <FileIcon />
  </div>
}

function RenderLinkCard(props: { word: WordTypes[WordType.LinkCard] }) {
  const { word } = props
  return <Link styleName="word-link-card" href={word.url}>
    <h3>{word.title}</h3>
    <div styleName="detail">
      <div styleName="desc">{word.desc}</div>
      <LazyImage src={formatAssetUrl(word.image_url)} />
    </div>
  </Link>
}

function RenderPersonCard(props: { word: WordTypes[WordType.PersonCard] }) {
  const { word } = props
  return <div styleName="word-person-card">
    <LazyImage src={word.avatar || defaultAvatar} />
    <div styleName="info">
      <div>{word.nickname}</div>
      <div styleName="corp">{word.corpname}</div>
    </div>
  </div>
}

function RenderMiniProgram(props: { word: WordTypes[WordType.MiniProgram] }) {
  const { word } = props
  return <div styleName="word-mini-program">
    <header>
      <LazyImage src={word.appicon} />
      <span>{word.appname}</span>
    </header>
    <div styleName="title">{word.title}</div>
    <div styleName="cover" style={{ backgroundImage: `url(${word.cover_path})` }}></div>
    <div styleName="footer">
      <MpIcon />
      小程序
    </div>
  </div>
}

function RenderVideonum(props: { word: WordTypes[WordType.Videonum] }) {
  const { word } = props
  return <div styleName="word-videonum">
    <div styleName="thumb" style={{ backgroundImage: `url(${word.cover_url})` }}></div>
    <div styleName="info">
      <LazyImage src={word.avatar} />
      <Typography.Paragraph ellipsis={{ rows: 2 }}>{word.desc}</Typography.Paragraph>
    </div>
    <div styleName="footer">
      <VideonumIcon />
      视频号
    </div>
  </div>
}

function RenderGroupInvite(props: { word: WordTypes[WordType.GroupInvite] }) {
  const { word } = props

  const [groupsMap] = useStreams(services.groups.list)
  const groups = useMemo(() => {
    // 优先使用新增的 roomInfos 字段显示预览；没有的话采用老方式，从当前账号的群列表里拉取信息
    if (word.roomInfos) {
      return word.roomInfos.map(info => ({
        type: ContactType.group as const,
        id: info.conversationId,
        ownerWxid: '',
        userId: '',
        creatorWxid: '',
        isOwner: false,
        ...info,
      }))
    }

    const mapByConversationId = groupsMap.mapEntries(([, group]) => [
      group.conversationId,
      { ...group, ownerWxid: '' },
    ])

    return word.Rooms.map(conversationId => mapByConversationId.get(conversationId)).filter(
      (group): group is GroupContact => !!group,
    )
  }, [word, groupsMap])

  return <div styleName="word-group-invite">
    {groups.map(group => <GroupItem key={group.id} contact={group} />)}
  </div>
}

function RenderGroupAt(props: {
  word: WordTypes[WordType.GroupAt]
  onUpdate?: (item: WordTypes[WordType.GroupAt]) => void
}) {
  const { word, onUpdate } = props

  if (!onUpdate) {
    return <div styleName="word-text">{word.text}</div>
  }

  return <Input.TextArea
    styleName="word-text-editable"
    value={word.text}
    onChange={e => onUpdate({ ...word, text: e.target.value })}
    rows={5}
  />
}
